﻿/***************************************************************
*       
* add by OceanHo 2015/8/26 11:00:33
*
****************************************************************/

using oceanho.webapi.factory.httpCore;
using oceanho.webapi.factory.httpCore.ReqKernal;
using oceanho.webapi.factory.httpEntity;
using oceanho.webapi.factory.httpUtility;
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

namespace oceanho.webapi.factory.httpModule
{
    /// <summary>
    /// http请求连接管理（用于过滤所有的请求,防慢放,DDOS攻击）模块
    /// </summary>
    public class http_service_module : IHttpModule
    {
        #region IHttpModule

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        public void Init(HttpApplication context)
        {
            CurrentHTTPApp = context;
            context.BeginRequest += context_BeginRequest;
            context.EndRequest += context_EndRequest;
        }

        private void context_BeginRequest(object sender, EventArgs e)
        {
            RunModuleEvent arg = RunEventUtility.CreateModuleEvent();

            ReqCache reqCache = null;
            //-> 步骤 1,2
            //-> 调用OnBeginValidConnect方法
            //-> 如果验证请求不合法,将调用 OnConnectFeelExistProblem() 方法。
            //-> 如果验证请求是合法的,将调用 OnValidConnectCompleted() 方法。
            //-> 如果 OnBeginValidConnect 返回false,表示验证没有通过,直接结束本次请求
            if (!OnBeginValidConnect(arg, ref reqCache))
            {
                EndReqAsBadRequest();
                return;
            }

            // set 3
            OnValidConnectCompleted(CurrentHTTPApp, reqCache);
        }

        private void context_EndRequest(object sender, EventArgs e)
        {
            ReqCacheDbUtility.ReduceReqWaitCount((HttpApplication)sender);
        }
        #endregion

        #region -- 自定义属性 --

        /// <summary>
        /// 获取当前的HttpApplication对象
        /// </summary>
        protected HttpApplication CurrentHTTPApp
        {
            get;
            private set;
        }

        #endregion

        #region -- 自定义方法 -- EndRequest/ClearAndEndRequest --

        /// <summary>
        /// 跳过管线链中的所有事件和筛选并直接执行 System.Web.HttpApplication.EndRequest()方法
        /// </summary>
        protected void EndRequest()
        {
            CurrentHTTPApp.CompleteRequest();
        }

        /// <summary>
        /// 清除所有缓存区内容，并跳过管线链中的所有事件和筛选并直接执行 System.Web.HttpApplication.EndRequest()方法
        /// </summary>
        protected void ClearAndEndRequest()
        {
            CurrentHTTPApp.Response.Clear();
            CurrentHTTPApp.Response.ClearContent();
            EndRequest();
        }

        /// <summary>
        /// 输出Bad Request，以 502 HTTP 状态码结束本次请求，此方法可以被重写
        /// </summary>
        protected virtual void EndReqAsBadRequest()
        {
            CurrentHTTPApp.Response.StatusCode = 502;
            CurrentHTTPApp.Response.Write("Bad Request");
            CurrentHTTPApp.Response.Flush();
            EndRequest();
        }
        #endregion

        #region -- 自定义方法 -- OnBeginValidConnect/OnConnectFeelExistProblem/OnValidConnectCompleted --

        /// <summary>
        /// 定义一个标识当 http_service_module 模块开始校验客户端请求的时的方法
        /// 注意：如果请求验证通过，需要返回True。如果请求验证失败，返回false，程序将直接调用EndRequest()方法结束本次请求处理
        /// </summary>
        /// <param name="arg">RunModuleEvent参数，可以通过修改Handled来改变是否需要执行系统自定义的校验</param>
        /// <param name="reqCache">输出参数（当前请求的reqCache对象）</param>
        protected virtual bool OnBeginValidConnect(RunModuleEvent arg, ref ReqCache reqCache)
        {
            // 验证IP是否可访问,会自动增加挂起次数,单位时间访问次数
            if (!ReqCacheDbUtility.IsValid(CurrentHTTPApp, out reqCache))
            {
                arg.Handled = false;
                // 如果请求感觉不合法,调用OnConnectFeelExistProblem函数
                OnConnectFeelExistProblem(arg, reqCache);
                return !(arg.Handled);
            }
            return true;
        }

        /// <summary>
        /// 定义一个标识当 http_service_module 模块检测到某个请求客户端可能存在问题时执行的方法
        /// </summary>
        /// <param name="arg">RunModuleEvent参数，可以通过修改Handled来改变是否需要执行系统自定义的校验</param>
        /// <param name="reqCache">可能存在问题的ReqCache对象，此对象保存在Memcached中</param>
        protected virtual void OnConnectFeelExistProblem(RunModuleEvent arg, ReqCache reqCache)
        {
            arg.Handled = true;
        }


        /// <summary>
        /// 定义一个标识当 http_service_module 模块成功校验客户端请求的时的方法，您可以在此方法里面订阅任何非BeginRequest事件
        /// </summary>
        /// <param name="app">HttpApplication上下文对象</param>
        /// <param name="reqCache">当前请求的ReqCache对象</param>
        protected virtual void OnValidConnectCompleted(HttpApplication app, ReqCache reqCache)
        {
        }
        #endregion
    }
}
